package xyz.hubery.match.impl;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MimeTypeUtils;
import xyz.hubery.domain.EntrustOrder;
import xyz.hubery.domain.Order;
import xyz.hubery.enums.OrderDirection;
import xyz.hubery.enums.OrderStatus;
import xyz.hubery.match.MatchService;
import xyz.hubery.model.*;
import xyz.hubery.stream.Source;
import xyz.hubery.util.DepthCacheManager;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;

/**
 * 限价交易服务接口实现类
 */
@Service
@Slf4j
public class LimitMatchServiceImpl implements MatchService {
    @Autowired
    private DepthCacheManager depthCacheManager;

    private final Source source;

    // 构造器注入
    public LimitMatchServiceImpl(Source source) {
        this.source = source;
        log.info(" Source 注入成功");
    }

//    @PostConstruct
//    public void init() {
//        log.info("=== Stream Configuration Check ===");
//        log.info("Source instance: {}", source);
//        if (source != null) {
//            log.info("tradePlateOut channel: {}", source.tradePlateOut());
//            log.info("exchangeTradesOut channel: {}", source.exchangeTradesOut());
//        } else {
//            log.error(" Source is NULL - 依赖注入失败");
//        }
//        log.info("=== Stream Configuration Check End ===");
//    }


    /**
     * 吃单
     *
     * @param orderBooks 该交易对下的账本
     * @param order      当前需要处理的订单
     */
    @Override
    public void match(OrderBooks orderBooks, Order order) {
        log.info("开始撮合交易订单：{}" + order.getOrderId());

        // 处理前先判断订单状态
        if(order.getOrderStatus() == OrderStatus.CANCELED.getCode()){ // 当前订单为撤销单
            boolean canceled = orderBooks.cancelOrder(order);
            if(canceled) { // 删除成功，通知交易服务更新委托单状态
                MessageBuilder<String> builder = MessageBuilder.withPayload(order.getOrderId()).setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON);
                source.cancelOrderOut().send(builder.build());
            }
            return;
        }

        // 1、校验数据
        if (order.getPrice().compareTo(BigDecimal.ZERO) <= 0) { // 非法价格
            return;
        }

        // 已经完成的订单
//        List<Order> completedOrders = new ArrayList<>();

        // 产生的交易记录
        List<ExchangeTrade> exchangeTrades = new ArrayList<>();

        boolean exit = false; // 判断当前订单是否吃完
        // 2、获取挂单数据
        Integer direction = order.getOrderDirection();
        direction = direction == 1 ? 2 : 1; // 翻转一下方向
        // 如皋该订单是买单，则获取卖单数据，否则获取买单数据
        TreeMap<BigDecimal, MergeOrder> currentOrders
                = orderBooks.getCurrentOrders(OrderDirection.getDirectionByCode(direction));
        // 3、遍历挂单数据
        Set<BigDecimal> markerPriceSet = currentOrders.keySet(); //挂单价格容器列表
        for (BigDecimal markerPrice : markerPriceSet) {
            // 4、如果当前订单的价格与目前挂单的价格不匹配，则退出（太贵了或太便宜了）
            if (order.getOrderDirection() == OrderDirection.BUY.getCode() && order.getPrice().compareTo(markerPrice) < 0) {
                break;
            }
            if (order.getOrderDirection() == OrderDirection.SELL.getCode() && order.getPrice().compareTo(markerPrice) > 0) {
                break;
            }
            // 5、否则，遍历列表，进行订单匹配（吃单
            MergeOrder mergeOrder = currentOrders.get(markerPrice);
            Iterator<Order> iterator = mergeOrder.iterator();
            while (iterator.hasNext()) {
                Order marker = iterator.next(); // 当前挂单的订单数据

                // 匹配订单
                ExchangeTrade exchangeTrade = processMatch(order, marker, orderBooks);
                if (exchangeTrade != null) {
                    exchangeTrades.add(exchangeTrade);
                }

                // 目前挂单的订单是否全部交易
                if (marker.isCompleted()) {
//                    completedOrders.add(marker);
//                    orderBooks.cancelOrder(marker);// 去除当前挂单数据(这样操作会重复扣减盘口数量
                    iterator.remove();
                }

                // 判断是否吃饱
                if (order.isCompleted()) {
//                    completedOrders.add(order);
                    exit = true;
                    break;
                }
            }
            if (exit) { // 已经吃饱了
                break;
            }
        }

        // 6、如果订单没有完成，加入挂单列表，等待别人来吃单
        if (!order.isCompleted()) {
            orderBooks.addOrder(order);
        }

        // 7 发送交易记录
        if (!CollectionUtils.isEmpty(exchangeTrades)) {
            handlerExchangeTrades(exchangeTrades);
        }
//        if (!completedOrders.isEmpty()) {
        // 8 发送已经成交的交易记录
//            completedOrders(completedOrders);
//    }
        // 更新盘口
        TradePlate tradePlate = order.getOrderDirection() == OrderDirection.BUY.getCode() ?
                orderBooks.getBuyTradePlate() : orderBooks.getSellTradePlate();
        // 更新缓存中的盘口数据
        depthCacheManager.updateDepthSnapshot(orderBooks.getSymbol(),orderBooks.getBuyTradePlate() ,orderBooks.getSellTradePlate());

        // 9 发送盘口数据，用于前端变化（ 使用中间件稍有延迟
        sendTradePlateData(tradePlate);
    }


    /**
     * 发送盘口数据
     *
     * @param tradePlate
     */
    private void sendTradePlateData(TradePlate tradePlate) {
        if(tradePlate == null) {
            return;
        }
        Message<TradePlate> message = MessageBuilder
                .withPayload(tradePlate)
                .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON)
                .build();
        source.tradePlateOut().send(message);
    }

//    /**
//     * 完成的订单
//     * @param completedOrders
//     */
//    private void completedOrders(List<Order> completedOrders) {
//        Message<List<Order>> message = MessageBuilder
//                .withPayload(completedOrders)
//                .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON)
//                .build();
//        source.completedOrdersOut().send(message);
//    }

    /**
     * 处理成交的记录
     *
     * @param exchangeTrades
     */
    private void handlerExchangeTrades(List<ExchangeTrade> exchangeTrades) {
        log.info("本次成交的记录为:{}", exchangeTrades);

        // 检查 source
        if (source == null) {
            log.error("Message source is null, cannot send exchange trades");
            return;
        }
        Message<List<ExchangeTrade>> message = MessageBuilder
                .withPayload(exchangeTrades)
                .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON)
                .build();
        source.exchangeTradesOut().send(message);

    }

    /**
     * 订单的匹配
     *
     * @param taker      吃单
     * @param marker     挂单
     * @param orderBooks 账本数据
     * @return 成交记录
     */
    private ExchangeTrade processMatch(Order taker, Order marker, OrderBooks orderBooks) {
        // 1、定义本次交易的变量
        BigDecimal takerVolume = taker.getVolume().subtract(taker.getTradedVolume()); // 吃单交易量
        BigDecimal markerVolume = marker.getVolume().subtract(marker.getTradedVolume()); // 挂单交易量
        BigDecimal dealPrice = marker.getPrice(); //  本次交易的价格
        BigDecimal dealVolume = takerVolume.compareTo(markerVolume) < 0 ? takerVolume : markerVolume; //实际交易量
        BigDecimal turnover = dealPrice.multiply(dealVolume).setScale(orderBooks.getBaseCoinScale(), RoundingMode.HALF_UP);
        ; // 本次成交额
        if (dealVolume.compareTo(BigDecimal.ZERO) <= 0) { // 交易量不合法
            return null;
        }

        // 设置吃单成交数据
        taker.setTradedVolume(taker.getTradedVolume().add(dealVolume));
        taker.setTurnover(taker.getTurnover().add(turnover));

        // 设置挂单成交数据
        marker.setTradedVolume(marker.getTradedVolume().add(dealVolume));
        marker.setTurnover(marker.getTurnover().add(turnover));

        // 新建一条成交记录
        ExchangeTrade exchangeTrade = new ExchangeTrade();
        exchangeTrade.setSymbol(orderBooks.getSymbol());
        exchangeTrade.setDirection(OrderDirection.getDirectionByCode(taker.getOrderDirection()));
        exchangeTrade.setPrice(dealPrice);
        exchangeTrade.setVolume(dealVolume);
        exchangeTrade.setBuyOrderId(taker.getOrderId());
        exchangeTrade.setSellOrderId(marker.getOrderId());
        exchangeTrade.setBuyTurnover(turnover);
        exchangeTrade.setSellTurnover(turnover);
        exchangeTrade.setTime(System.currentTimeMillis());


        /**
         * 处理盘口:
         *  我们的委托单: 将挂单的数据做了一部分消耗
         */
        if (marker.getOrderDirection() == OrderDirection.BUY.getCode()) {
            // 减少挂单的数据量
            orderBooks.getBuyTradePlate().remove(marker, dealVolume);
        } else {
            orderBooks.getSellTradePlate().remove(marker, dealVolume);
        }

        return exchangeTrade;
    }


}
